#include "MLCD.h"
#include "mf_spi.h"
#include <string.h>

static mlcd_t mlcd_panel;

static const uint16_t Font7x10[] = {
    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // sp
    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x0000, 0x0000, // !
    0x2800, 0x2800, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // "
    0x2400, 0x2400, 0x7C00, 0x2400, 0x4800, 0x7C00, 0x4800, 0x4800, 0x0000, 0x0000, // #
    0x3800, 0x5400, 0x5000, 0x3800, 0x1400, 0x5400, 0x5400, 0x3800, 0x1000, 0x0000, // $
    0x2000, 0x5400, 0x5800, 0x3000, 0x2800, 0x5400, 0x1400, 0x0800, 0x0000, 0x0000, // %
    0x1000, 0x2800, 0x2800, 0x1000, 0x3400, 0x4800, 0x4800, 0x3400, 0x0000, 0x0000, // &
    0x1000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // '
    0x0800, 0x1000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x2000, 0x1000, 0x0800, // (
    0x2000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x1000, 0x2000, // )
    0x1000, 0x3800, 0x1000, 0x2800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // *
    0x0000, 0x0000, 0x1000, 0x1000, 0x7C00, 0x1000, 0x1000, 0x0000, 0x0000, 0x0000, // +
    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x1000, // ,
    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x3800, 0x0000, 0x0000, 0x0000, 0x0000, // -
    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, // .
    0x0800, 0x0800, 0x1000, 0x1000, 0x1000, 0x1000, 0x2000, 0x2000, 0x0000, 0x0000, // /
    0x3800, 0x4400, 0x4400, 0x5400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 0
    0x1000, 0x3000, 0x5000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // 1
    0x3800, 0x4400, 0x4400, 0x0400, 0x0800, 0x1000, 0x2000, 0x7C00, 0x0000, 0x0000, // 2
    0x3800, 0x4400, 0x0400, 0x1800, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 3
    0x0800, 0x1800, 0x2800, 0x2800, 0x4800, 0x7C00, 0x0800, 0x0800, 0x0000, 0x0000, // 4
    0x7C00, 0x4000, 0x4000, 0x7800, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 5
    0x3800, 0x4400, 0x4000, 0x7800, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 6
    0x7C00, 0x0400, 0x0800, 0x1000, 0x1000, 0x2000, 0x2000, 0x2000, 0x0000, 0x0000, // 7
    0x3800, 0x4400, 0x4400, 0x3800, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // 8
    0x3800, 0x4400, 0x4400, 0x4400, 0x3C00, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // 9
    0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, // :
    0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x1000, 0x1000, 0x1000, // ;
    0x0000, 0x0000, 0x0C00, 0x3000, 0x4000, 0x3000, 0x0C00, 0x0000, 0x0000, 0x0000, // <
    0x0000, 0x0000, 0x0000, 0x7C00, 0x0000, 0x7C00, 0x0000, 0x0000, 0x0000, 0x0000, // =
    0x0000, 0x0000, 0x6000, 0x1800, 0x0400, 0x1800, 0x6000, 0x0000, 0x0000, 0x0000, // >
    0x3800, 0x4400, 0x0400, 0x0800, 0x1000, 0x1000, 0x0000, 0x1000, 0x0000, 0x0000, // ?
    0x3800, 0x4400, 0x4C00, 0x5400, 0x5C00, 0x4000, 0x4000, 0x3800, 0x0000, 0x0000, // @
    0x1000, 0x2800, 0x2800, 0x2800, 0x2800, 0x7C00, 0x4400, 0x4400, 0x0000, 0x0000, // A
    0x7800, 0x4400, 0x4400, 0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x0000, 0x0000, // B
    0x3800, 0x4400, 0x4000, 0x4000, 0x4000, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // C
    0x7000, 0x4800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4800, 0x7000, 0x0000, 0x0000, // D
    0x7C00, 0x4000, 0x4000, 0x7C00, 0x4000, 0x4000, 0x4000, 0x7C00, 0x0000, 0x0000, // E
    0x7C00, 0x4000, 0x4000, 0x7800, 0x4000, 0x4000, 0x4000, 0x4000, 0x0000, 0x0000, // F
    0x3800, 0x4400, 0x4000, 0x4000, 0x5C00, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // G
    0x4400, 0x4400, 0x4400, 0x7C00, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // H
    0x3800, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x3800, 0x0000, 0x0000, // I
    0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // J
    0x4400, 0x4800, 0x5000, 0x6000, 0x5000, 0x4800, 0x4800, 0x4400, 0x0000, 0x0000, // K
    0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x7C00, 0x0000, 0x0000, // L
    0x4400, 0x6C00, 0x6C00, 0x5400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // M
    0x4400, 0x6400, 0x6400, 0x5400, 0x5400, 0x4C00, 0x4C00, 0x4400, 0x0000, 0x0000, // N
    0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // O
    0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x4000, 0x4000, 0x4000, 0x0000, 0x0000, // P
    0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x5400, 0x3800, 0x0400, 0x0000, // Q
    0x7800, 0x4400, 0x4400, 0x4400, 0x7800, 0x4800, 0x4800, 0x4400, 0x0000, 0x0000, // R
    0x3800, 0x4400, 0x4000, 0x3000, 0x0800, 0x0400, 0x4400, 0x3800, 0x0000, 0x0000, // S
    0x7C00, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // T
    0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // U
    0x4400, 0x4400, 0x4400, 0x2800, 0x2800, 0x2800, 0x1000, 0x1000, 0x0000, 0x0000, // V
    0x4400, 0x4400, 0x5400, 0x5400, 0x5400, 0x6C00, 0x2800, 0x2800, 0x0000, 0x0000, // W
    0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x2800, 0x2800, 0x4400, 0x0000, 0x0000, // X
    0x4400, 0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // Y
    0x7C00, 0x0400, 0x0800, 0x1000, 0x1000, 0x2000, 0x4000, 0x7C00, 0x0000, 0x0000, // Z
    0x1800, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1800, // [
    0x2000, 0x2000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0000, 0x0000, /* \ */
    0x3000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x3000, // ]
    0x1000, 0x2800, 0x2800, 0x4400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ^
    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFE00, // _
    0x2000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // `
    0x0000, 0x0000, 0x3800, 0x4400, 0x3C00, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // a
    0x4000, 0x4000, 0x5800, 0x6400, 0x4400, 0x4400, 0x6400, 0x5800, 0x0000, 0x0000, // b
    0x0000, 0x0000, 0x3800, 0x4400, 0x4000, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // c
    0x0400, 0x0400, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // d
    0x0000, 0x0000, 0x3800, 0x4400, 0x7C00, 0x4000, 0x4400, 0x3800, 0x0000, 0x0000, // e
    0x0C00, 0x1000, 0x7C00, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // f
    0x0000, 0x0000, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0400, 0x7800, // g
    0x4000, 0x4000, 0x5800, 0x6400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // h
    0x1000, 0x0000, 0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // i
    0x1000, 0x0000, 0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0xE000, // j
    0x4000, 0x4000, 0x4800, 0x5000, 0x6000, 0x5000, 0x4800, 0x4400, 0x0000, 0x0000, // k
    0x7000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, // l
    0x0000, 0x0000, 0x7800, 0x5400, 0x5400, 0x5400, 0x5400, 0x5400, 0x0000, 0x0000, // m
    0x0000, 0x0000, 0x5800, 0x6400, 0x4400, 0x4400, 0x4400, 0x4400, 0x0000, 0x0000, // n
    0x0000, 0x0000, 0x3800, 0x4400, 0x4400, 0x4400, 0x4400, 0x3800, 0x0000, 0x0000, // o
    0x0000, 0x0000, 0x5800, 0x6400, 0x4400, 0x4400, 0x6400, 0x5800, 0x4000, 0x4000, // p
    0x0000, 0x0000, 0x3400, 0x4C00, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0400, 0x0400, // q
    0x0000, 0x0000, 0x5800, 0x6400, 0x4000, 0x4000, 0x4000, 0x4000, 0x0000, 0x0000, // r
    0x0000, 0x0000, 0x3800, 0x4400, 0x3000, 0x0800, 0x4400, 0x3800, 0x0000, 0x0000, // s
    0x2000, 0x2000, 0x7800, 0x2000, 0x2000, 0x2000, 0x2000, 0x1800, 0x0000, 0x0000, // t
    0x0000, 0x0000, 0x4400, 0x4400, 0x4400, 0x4400, 0x4C00, 0x3400, 0x0000, 0x0000, // u
    0x0000, 0x0000, 0x4400, 0x4400, 0x2800, 0x2800, 0x2800, 0x1000, 0x0000, 0x0000, // v
    0x0000, 0x0000, 0x5400, 0x5400, 0x5400, 0x6C00, 0x2800, 0x2800, 0x0000, 0x0000, // w
    0x0000, 0x0000, 0x4400, 0x2800, 0x1000, 0x1000, 0x2800, 0x4400, 0x0000, 0x0000, // x
    0x0000, 0x0000, 0x4400, 0x4400, 0x2800, 0x2800, 0x1000, 0x1000, 0x1000, 0x6000, // y
    0x0000, 0x0000, 0x7C00, 0x0800, 0x1000, 0x2000, 0x4000, 0x7C00, 0x0000, 0x0000, // z
    0x1800, 0x1000, 0x1000, 0x1000, 0x2000, 0x2000, 0x1000, 0x1000, 0x1000, 0x1800, // {
    0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, // |
    0x3000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x1000, 0x1000, 0x1000, 0x3000, // }
    0x0000, 0x0000, 0x0000, 0x7400, 0x4C00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // ~
    0x0000, 0x0800, 0x1400, 0x1400, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // custom
};
FontType Font_7x10 = {7, 10, Font7x10};

/**
 * @brief mlcd_clear
 *
 * @param void
 * @return None
 */
void mlcd_clear(void)
{
    uint8_t clear_data[2] = {BIT_CLEAR, 0x00}; /* clear cmd */
    DISP_SET;                                  /* set dis_pin */
    CS_SET;                                    /* set cs_pin */
#if MLCD_DMA_USE
    spi1_dma_trans(clear_data, 2); /* write clear_cmd */
#else
    mf_spi_write(clear_data, 2); /* write clear_cmd */
#endif
    CS_RESET; /* reset cs_pin */
}

/**
 * @brief mlcd_draw_pixel
 *
 * @param pixel_x 0-127
 * @param pixel_y 0-127
 * @param pixel_color mlcd_color
 * @return None
 */
void mlcd_draw_pixel(uint8_t pixel_x, uint8_t pixel_y, mlcd_color pixel_color)
{
    if (pixel_x >= MLCD_WIDTH || pixel_y >= MLCD_HEIGHT)
    {
        return;
    }

    /*
     * 新缓冲区布局：
     * 每行 50 字节：2 字节行地址（忽略）+ 48 字节显示数据
     */
    uint16_t y_offset = 50 * pixel_y; // 每行总字节偏移
    uint8_t group = pixel_x / 8;      // 每 8 个像素为一组
    uint8_t group_offset = pixel_x % 8;

    // 像素数据部分起始偏移量为 y_offset + 2
    uint16_t pixel_data_offset = y_offset + 2 + group * 3;

    switch (group_offset)
    {
    case 0:
        mlcd_panel.Buffer[pixel_data_offset] &= 0b11111000;
        mlcd_panel.Buffer[pixel_data_offset] |= (pixel_color << 0);
        break;
    case 1:
        mlcd_panel.Buffer[pixel_data_offset] &= 0b11000111;
        mlcd_panel.Buffer[pixel_data_offset] |= (pixel_color << 3);
        break;
    case 2:
        mlcd_panel.Buffer[pixel_data_offset] &= 0b00111111;
        mlcd_panel.Buffer[pixel_data_offset + 1] &= 0b11111110;
        mlcd_panel.Buffer[pixel_data_offset] |= (pixel_color << 6);
        mlcd_panel.Buffer[pixel_data_offset + 1] |= (pixel_color >> 2);
        break;
    case 3:
        mlcd_panel.Buffer[pixel_data_offset + 1] &= 0b11110001;
        mlcd_panel.Buffer[pixel_data_offset + 1] |= (pixel_color << 1);
        break;
    case 4:
        mlcd_panel.Buffer[pixel_data_offset + 1] &= 0b10001111;
        mlcd_panel.Buffer[pixel_data_offset + 1] |= (pixel_color << 4);
        break;
    case 5:
        mlcd_panel.Buffer[pixel_data_offset + 1] &= 0b01111111;
        mlcd_panel.Buffer[pixel_data_offset + 2] &= 0b11111100;
        mlcd_panel.Buffer[pixel_data_offset + 1] |= (pixel_color << 7);
        mlcd_panel.Buffer[pixel_data_offset + 2] |= (pixel_color >> 1);
        break;
    case 6:
        mlcd_panel.Buffer[pixel_data_offset + 2] &= 0b11100011;
        mlcd_panel.Buffer[pixel_data_offset + 2] |= (pixel_color << 2);
        break;
    case 7:
        mlcd_panel.Buffer[pixel_data_offset + 2] &= 0b00011111;
        mlcd_panel.Buffer[pixel_data_offset + 2] |= (pixel_color << 5);
        break;
    default:
        break;
    }
}

/**
 * @brief mlcd_refresh
 *
 * @param void
 * @return None
 */
void mlcd_refresh(void)
{
    if (mlcd_panel.msg.msg_transport == MLCD_PASS)
    {
        /* refresh way -> Multiple Lines start */
        CS_SET;
#if MLCD_DMA_USE
#if MLCD_MULTIPLE_REFRESH
        spi1_dma_trans(mlcd_panel.Buffer, MLCD_DIS_BUFF_SIZE); /* write line data */
#else
        spi1_dma_trans(&mlcd_panel.Buffer[0], 1);
        for (uint8_t line = 1; line <= MLCD_HEIGHT; line++)
        {
            if (line != 1)
            {
                spi1_dma_trans(mlcd_panel.Buffer + (50 * line), 1); /* write dmy->null */
            }
            spi1_dma_trans(&line, 1);                                /* write line_address */
            spi1_dma_trans(mlcd_panel.Buffer + 2 + (50 * line), 48); /* write line data */
        }
        spi1_dma_trans(mlcd_panel.Buffer + MLCD_DIS_BUFF_SIZE - 2, 2);
#endif
#else
#if MLCD_MULTIPLE_REFRESH
        mf_spi_write(mlcd_panel.Buffer, MLCD_DIS_BUFF_SIZE); /* write line data */
#else
        mf_spi_write(&mlcd_panel.Buffer[0], 1);
        for (uint8_t line = 1; line <= MLCD_HEIGHT; line++)
        {
            if (line != 1)
            {
                mf_spi_write(mlcd_panel.Buffer + (50 * line), 1); /* write dmy->null */
            }
            mf_spi_write(&line, 1);                                /* write line_address */
            mf_spi_write(mlcd_panel.Buffer + 2 + (50 * line), 48); /* write line data */
        }
        mf_spi_write(mlcd_panel.Buffer + MLCD_DIS_BUFF_SIZE - 2, 2);
#endif
#endif
        CS_RESET;
        /* refresh way -> Multiple Lines end */
        mlcd_panel.msg.msg_transport = MLCD_FAIL; /* clear mlcd_refresh_IT */
    }
    else
    {
        /* NULL */
    }
}

/**
 * @brief mlcd_semaphore_frame
 *
 * @param void
 * @return None
 */
void mlcd_semaphore_frame(void)
{
    mlcd_panel.msg.msg_transport = MLCD_PASS;
}

/**
 * @brief mlcd_init
 *
 * @param void
 * @return None
 */
void mlcd_init(void)
{
    mlcd_panel.Buffer[0] = MLCD_WRITE_CMD;
    mlcd_clear();                                        /* clear screen */
    memset(mlcd_panel.Buffer, 0xFF, MLCD_DIS_BUFF_SIZE); /* clear buff */
}

/**
 * @brief mlcd_memset_buff
 *
 * @param void
 * @return None
 */
void mlcd_memset_buff(void)
{
    memset(mlcd_panel.Buffer, 0xFF, MLCD_DIS_BUFF_SIZE); /* clear buff */
    mlcd_panel.Buffer[0] = MLCD_WRITE_CMD;               /* set multiple line cmd */
    for (uint8_t i = 1; i <= MLCD_HEIGHT; i++)           /* update line address to buff */
    {
        mlcd_panel.Buffer[1 + i * 50] = i;
    }
}

// 画空心矩形，(x, y) 是矩形的左上角坐标，width 和 height 是矩形的宽度和高度
void drawFrame(int x, int y, int width, int height, mlcd_color color)
{
    // 绘制矩形的四条边
    for (int i = x; i < x + width; i++)
    {
        mlcd_draw_pixel(i, y, color);              // 顶边
        mlcd_draw_pixel(i, y + height - 1, color); // 底边
    }

    for (int j = y; j < y + height; j++)
    {
        mlcd_draw_pixel(x, j, color);             // 左边
        mlcd_draw_pixel(x + width - 1, j, color); // 右边
    }
}

// 画填充矩形，(x, y) 是矩形的左上角坐标，width 和 height 是矩形的宽度和高度
void drawBox(int x, int y, int width, int height, mlcd_color color)
{
    for (int j = y; j < y + height; j++)
    {
        for (int i = x; i < x + width; i++)
        {
            mlcd_draw_pixel(i, j, color);
        }
    }
}

void mlcd_WriteChar(char ch, uint8_t pixel_x, uint8_t pixel_y, FontType Font, mlcd_color color)
{
    uint32_t i, b, j;
    if (ch < 32 || ch > 126)
        return;

    for (i = 0; i < Font.FontHeight; i++)
    {
        b = Font.data[(ch - 32) * Font.FontHeight + i];
        for (j = 0; j < Font.FontWidth; j++)
        {
            if ((b << j) & 0x8000)
            {
                mlcd_draw_pixel(pixel_x + j, (pixel_y + i), color);
            }
        }
    }
}

void mlcd_WriteString(const char *str, uint8_t pixel_x, uint8_t pixel_y, FontType Font, mlcd_color color)
{
    uint8_t char_spacing = 1; // 字符之间的间隔像素，可根据需要调整
    uint8_t cursor_x = pixel_x;

    // 遍历字符串
    while (*str)
    {
        char ch = *str;

        // 检查是否换行 (这里假定换行符 '\n')
        if (ch == '\n')
        {
            pixel_y += Font.FontHeight + char_spacing; // 换行时移动到下一行
            cursor_x = pixel_x;                        // 水平位置回到行起始点
        }
        else if (ch >= 32 && ch <= 126) // 可显示字符范围
        {
            // 显示字符
            mlcd_WriteChar(ch, cursor_x, pixel_y, Font, color);

            // 更新光标位置
            cursor_x += Font.FontWidth + char_spacing;
        }

        // 移动到下一个字符
        str++;
    }
}
